Translated by visionNoob, KNU https://github.com/insurgent92/CS231N_17_KOR_SUB
시작하겠습니다.
이번 시간에는 nerural network를 학습시키는 방법을 심도깊게 살펴보겠습니다.
우선 공지사항먼저 전달드리자면 첫 번째 과제는 오늘까지입니다.
금일 11:59 p.m 까지 Canvas에 제출해주시기 바라며, 더불어 과제 2가 새롭게 나갈 것입니다.
그리고 여러분의 프로젝트 제안서는 25일까지 제출해 주시기 바랍니다.
아직 준비가 안되신 분들이면 서둘러 해주시기 바랍니다.
어느정도 구상한 분들이 계십니까? 몇 분정도 계시는군요
어떻게 해야할지 모르겠다 싶으면 TA 사무실로 방문해 주시면 도와드리겠습니다.
Piazza에 다른 연계 프로젝트도 있으니 확인해 주시기 바랍니다.
그리고 Justin이 만든 "Linear Layer에서의 Backporp", "vector/tensor 미분" 관련자료가 있으니 참고해주세요.
이 자료를 보시면 vector/matrix에서 backporp이 어떻게 동작하는지 이해하실 수 있을 것입니다.
Syllabus에 Lecture 4에 링크되어 있으니 보시면 됩니다.
지난 시간에 함수를 computational graph로 표현하는 방법을 배웠습니다. 어떤 함수든 이렇게 표현할 수 있습니다.
Neural network에 대해서 자세히 배웠습니다. 여기 보이는 것 처럼 Linear Layer들이 있었죠
비 선형 연산자들을 끼워넣으면서 여러 층으로 쌓았습니다.
지난 시간에 CNN도 배웠습니다.
CNN은 "Spatial structure" 를 사용하기 위해 Conv Layer를 사용하는 NN의 특수한 형태이입니다.
Conv 필터가 입력 이미지를 슬라이딩해서 계산한 값들이 모여 출력 Activation map을 만들게 됩니다.
그리고 Conv layer는 대게 다수의 필터를 사용했습니다. 각 필터는 서로 다른 Activation map을 생성합니다.
여기 그림은 보면 어떤 입력이 있고 Depth가 있습니다. 이제 Activation map 을 만들어야 하는데
Activation map는 필터의 갯수만큼 존재하며 각 map은 입력의 공간적인 정보를 보존하고 있습니다.
자 이제 우리가 하고싶은 것은 가중치, 즉 파라미터를 업데이트 하고 싶은 것입니다.
지난 시간에 배웠듯이 Optimization을 통해서 네트워크의 파라미터를 학습할 수 있었습니다.
그리고 Loss 라는 산에서 Loss가 줄어드는 방향으로 이동하고 싶은 것입니다.
그리고 이는 gradient의 반대 방향으로 이동하는 것과 같습니다.
이를 Mini-batch Stochastic Gradient Descent 이라고 했습니다.
Mini-batch SGD는 우선 데이터의 일부만 가지고 Forword pass를 수행한 뒤에 Loss를 계산합니다.
그리고 gradient를 계산하기 위해서 backprop를 수행합니다.
그리고 그 gradient를 이용해서 파마미터를 업데이트합니다.
향 후 몇번의 강의는 NN의 학습에 대해 다룰 것입니다.
우선 NN 학습을 처음 시작할때 필요한 기본 설정에 대해 알아볼 것입니다.
활성함수 선택, 데이터 전처리, 가중치 초기화 Regularization, gradient checking 등이 이에 속하죠
또한 Training dynamics에 대해서도 다룰 것입니다. 학습이 잘 되고 있는지 확인하는 법에 대해 배울 것입니다.
어떤 방식으로 파라미터를 업데이트할 것인지에 대해서도 배울 것이고
가장 적절한 하이퍼파라미터를 찾기 위해 하이퍼파라미터를 Optimization 하는 방법도 배울 것입니다.
그리고 평가에 대해서도 이야기 할 것입니다.
그리고 Model ensemble에 대해서도 배웁니다.
오늘은 Part 1 으로 활성함수, 데이터 전처리, 가중치 초기화, Batch Normalization
학습 과정 다루기, 하이퍼 파라미터 Optimization 을 배워 보도록 하겠습니다.
자 Activation Function 부터 시작해봅시다.
자 먼저 지난시간에 봤던 Layer를 살펴보면
데이터 입력이 들어오면 가중치와 곱합니다. FC 나 CNN이 될 수 있겠죠
그 다음 활성함수, 즉 비선형 연산을 거치게 됩니다.
활성함수의 예시를 보겠습니다.
지난 시간에 Sigmoid를 본적이 있죠 ReLU도 봤습니다.
이번 시간에는 좀 더 다양한 종류의 활성함수와 그들간의 Trade-off에 대해 다뤄보도록 하겠습니다.
자 우선 Sigmoid부터 보겠습니다. 우리한테 제일 만만한 놈이죠
Sigmoid는 이렇게 생겼습니다. 1/(1+e^-x) 이죠
얘가 하는 일은 각 입력을 받아서 그 입력을 [0, 1] 사이의 값이 되도록 해줍니다.
입력의 값이 크면 Sigmoid의 출력은 1에 가까울 것이고
값이 작으면 0에 가까울 것입니다.
0 근처 구간(rigime)을 보면 선형스럽습니다 (/ 모양) 그 쪽은 선형함수 같아 보입니다.
Sigmoid는 역사적으로 아주 유명했습니다.
왜냐하면 Sigmoid가 일종의, 뉴런의 firing rate를 saturation 시키는 것으로 해석할 수 있기 때문입니다.
어떤 값이 0에서 1 사이의 값을 가지면 이를 fireing rate라고 생각할 수 있을 것입니다.
나중에 ReLU 같은 것을 배울 것인데, 실제로 ReLU가 생물학적 타당성이 더 크다는게 밝혀졌지만
Sigmoid 또한 이런 식으로 해석할 수 있다는 점을 알고 넘어가면 될 것 같습니다.
요 녀석을 한번 자세히 살펴보면 문제점이 몇 가지 있습니다.
우선 Saturation되는게 gradient를 없앱니다. 이게 무슨 뜻일까요?
이 Sigmoid gate를 Computraional graph에서 한번 살펴보겠습니다.
여기 데이터 X가 있고 출력이 있습니다.
Backprop에서 Gradient는 어떻게될까요?
우선 dL/dSigma 가 있습니다. dL/dSigma가 밑으로 내려가죠 그리고 dX/dSigma를 곱하게 될 것입니다.
이 값이 local sigmoid function의 gradient가 될 것입니다.
그리고 이런 값들이 계속 연쇄적으로 Backporp될 것입니다.
그렇다면 질문입니다. X가 -10이면 어떻게 될까요? gradient는 어떻게 생기게 될까요?
네 맞습니다. 0이 되겠죠. gradient는 0이 됩니다.
Sigmoid에서 음의 큰 값이면 sigmoid가 flat하게 되고 gradient가 0이 되겠죠
그리고 이 값이 밑으로 내려갈 것입니다.
거의 0에 가까운 값이 backprob 될 것입니다.
이 부분에서 gradient가 죽어버리게 되고 밑으로 0이 계속 전달되게 됩니다.
그렇다면 X가 0이면 어떻까요? 맞습니다. 이 구간에서는 잘 동작할 것입니다.
아주 그럴싸한 gradient를 얻게 될 것이고 backprop 이 잘 될 것입니다.
그러면 X가 10이면 어떻까요?
X가 값이 큰 양수 일 경우에도 sigmoid가 flat하기 때문에 gradient들 다 죽이게 됩니다.
그러면 gradient가 잘 흐르지 않겠죠
두 번째 문제는 바로 sigmoid 의 출력이 zero centered 하지 않다는 것입니다.
이게 왜 문제인지 한번 알아보도록 하겠습니다.
자 그럼 뉴런의 입력이 항상 양수일 때 어떤 일이 벌어지는지 한번 살펴보겠습니다.
이 경우에 모든 X가 양수일 때 입니다.
이 x는 어떤 가중치랑 곱해지고 활성함수를 통과하겠죠
그럼 W에 대한 gradient를 한번 생각해볼까요?
이 Layer에서의 local gradient가 어떻게 될지 한번 생각해 보세요
우선 dL/d(활성함수) 를 계산하겠죠 그렇게 Loss가 내려가고
그리고 Local gradient가 있을텐데, 기본적으로 그냥 X입니다.
그렇다면 모든 X가 양수라는 것은 무슨뜻일까요?
누군가가 gradient가 전부 "양수" 가 된다고 하셨는데요. 정확히는 "전부 양수" 또는 "전부 음수" 입니다.
위에서 dL/df(활성함수) 가 넘어오겠죠 이 값이 음수 또는 양수가 될 것입니다.
어떤 임의의 gradient가 내려온다고 생각해 봅시다.
우선 local gradient는 이 값이랑 곱해질 것이고 dF(활성함수)/dW는, 그냥 X가 될 것입니다.
그렇게 되면 gradient의 부호는 그저 위에서 내려온 gradient의 부호과 같아 질 것입니다.
이것이 의미하는 바는 W가 모두 같은 방향으로만 움직일 것임을 의미하게 됩니다.
파라미터를 업데이트 할 때 다 같이 증가하거나 다 같이 감소하거나 할 수 밖에 없습니다.
여기서의 문제는 이런 gradient 업데이트는 아주 비효율적이라는 것입니다.
여기 W가 이차원인 예제가 있습니다. W에 대한 두개의 축으로 이루어져 있습니다.
전부 양수 또는 음수로 업데이트된다는 것을 해석해보면
이렇게 되면 gradient가 이동할 수 있는 방향은 4분면 중 이 두 영역뿐이 안 될 것입니다.
이 두 방향으로 밖에 gradient가 업데이트되지 않습니다.
그래서 이론적으로 가장 최적의 W 업데이트가 저 파란색 화살표이고
우리가 어떤 초기 시작점부터 내려간다고 했을 때 시작점은 빨간 화살표의 맨 처음 부분입니다.
하지만 gradient는 파란색 방향으로 내려갈 수 없습니다. 그 방향으로는 움직일 수 없기 때문이죠
그렇기 때문에 우리는 여러번 gradient 업데이트를 수행해 줘야 합니다.
가령 이렇게 빨간 화살표 방향와 같이, gradient가 이동 가능한 방향으로만 이동을 할 수 있게 됩니다.
이것이 바로 우리가 일반적으로 zero-mean data을 원하는 이유입니다.
입력 X가 양수/음수를 모두 가지고 있으면 전부 같은 방향으로 움직이는 일은 발생하지는 않을 것입니다.
이해 하셨습니까? 질문 있으신가요? 좋습니다.
자 우리는 지금까지 sigmoid가 가진 문제에 대해서 배웠습니다.
Saturation이 gradient를 죽일 수 있었습니다. 양/음의 방향으로 너무 큰 값을 가진다면 말이죠
그리고 zero-centered 가 아니기 때문에 gradient 업데이트가 효율적이지 않았습니다.
그리고 세 번째 문제는 exp()로 인해 계산비용이 크다는 것입니다.
이 세 번째 문제는 그렇게 큰 문제는 아닙니다.
큰 그림으로 봤을 때 다른 연산들, 가령 내적의 더 계산이 비쌉니다.
하지만 굳이 문제로 뽑자면 이렇다는 것입니다.
이제 두 번째 활성 함수를 한번 보겠습니다. tanh입니다.
sigmoid 랑 유사하죠. 하지만 범위가 [-1 , 1] 입니다.
가장 큰 차이점이 있다면 이제 zero-centered 라는 것입니다.
이를 통해 두 번째 문제가 해결됩니다. 하지만 saturation때문에 여전히 Gradient가 죽습니다.
여기 보시면 여전히 gradient가 평평해 지는 구간이 있겠죠
tanh는 sigmoid보다는 조금 낫지만 그래도 여전히 문제점은 있습니다.
자 이제 ReLU 를 살펴보겠습니다.
CNN 강의할 때 본 적 있으실 것입니다.
Conv layer들 사이 사이에 ReLU가 있었죠
ReLU의 함수는 f(x) = max(0,x) 입니다.
이 함수는 element-wise 연산을 수행하며 입력이 음수면 값이 0이 됩니다.
그리고 양수면 입력 값 그대로를 출력합니다.
ReLU는 상당히 자주 쓰입니다. 그리고 기존 sigmoid와 tanh한테 있었던 문제점들을 한번 살펴보자면
우선 ReLU는 양의 값에서는 saturetion되지 않습니다.
적어도 입력스페이스의 절반은 saturation 되지 않을 것입니다. ReLU의 가장 큰 장점이죠
그리고 계산 효율이 아주 뛰어납니다.
기존의 sigmoid는 함수 안에 지수 항이 있었죠
반면에 ReLU는 단순히 max 연산이므로 계산이 매우 빠릅니다.
ReLU를 사용하면 실제로 sigmoid나 tanh보다 수렴속도가 훨씬 빠릅니다. 거의 6배정도 더 빠르죠
그리고 사실 생물학적 타당성도 ReLU가 sigmoid보다 큽니다.
실제 신경과학적인 실험을 통해서 뉴런을 관찰해보면
실제 뉴런의 입/출력 값을 확인해보면 sigmoid보다 ReLU 스럽다는 것을 알 수 있습니다.
ImageNet 2012에서 우승한 AlexNet이 처음 ReLU를 사용하기 시작했습니다.
그런데 ReLU에 문제가 하나 있다면 ReLU는 더이상 zero-centered가 아니라는 것입니다.
tanh가 이 문제는 해결했는데 ReLU는 다시 이 문제를 가지고 있게 됩니다.
이는 ReLU가 가진 이슈중 하나입니다.
그리고 ReLU가 가진 골칫거리가 하나 더 있습니다.
ReLU에서 양의 수에서는 saturation 되지 않지만 음의 경우에서는 그렇지 않습니다.
좀 더 자세히 알아보겠습니다.
X가 -10이면 어떻게 될까요?
gradient가 0이 되겠죠
그렇다면 x가 10일때는 어떻게 될까요?
맞습니다. 선형 영역(linear regime)에 속합니다.
x가 0일 때는 어떨까요? 물론 여기에서는 정의되지 않지만 실제로는 0입니다.
기본적으로 ReLU는 gradient의 절반을 죽여버리는 셈입니다.
그래서 우리는 dead ReLU 라는 현상을 겪을 수 있습니다.
이런 일은 몇 가지 상황에서 발생할 수 있는데
자 여기 data cloud가 있습니다. 여러분이 가진 traing data라고 생각하시면 됩니다.
ReLU에서는 평면의 절반만 activate 된다는 것을 알 수 있습니다.
각 평면(초록 빨강 직선)이 각 ReLU를 의미한다고 보시면 됩니다.
그리고 ReLU가 data cloud에서 떨어져 있는 경우에 "dead ReLU" 가 발생할 수 있습니다.
dead ReLU에서는 activate 가 일어나지 않고 update되지 않습니다. 반면 active ReLU는
일부는 active되고 일부는 active하지 않을 것입니다.
몇 가지 이유로 이런 일이 발생할 수 있습니다.
우선 첫 번째로는 초기화를 잘 못한 경우 입니다.
여기 dead ReLU 처럼 생긴 경우인데, 가중치 평면이 data cloud에서 멀리 떨어져 있는 경우입니다.
이런 경우 어떤 데이터 입력에서도 activate 되는 경우가 존재하지 않을 것이고 backporp이 일어나지 않을 것입니다.
이런 경우 update되지도 activate되지도 않을 것입니다.
그리고 더 흔한 경우는 Leraning rate가 지나치게 높은 경우입니다.
처음에 "적절한 ReLU" 로 시작할 수 있다고 해도 만약 update를 지나치게 크게 해 버려 가중치가 날뛴다면
ReLU 가 데이터의 manifold를 벗어나게 됩니다. 이런 일들은 학습과정에서 충분히 일어날 수 있습니다.
그래서 처음에는 학습이 잘 되다가 갑자기 죽어버리는 경우가 생기는 것입니다.
그리고 실제로,
그리고 실제로 학습을 다 시켜놓은 네트워크를 살펴보면 10~20% 가량은 dead ReLU가 되어 있습니다.
이게 문제이긴 하지만 ReLU를 사용하고 있다면 대부분의 네트워크가 이 문제를 겪을 수 있습니다.
일부는 dead ReLU가 되버리는 것을 관찰해 볼 수 있습니다. 하지만 그 정도면 네트워크 학습에 크게 지장이 있진 않습니다.
질문 있나요?
[학생이 질문]
네 맞습니다. Data cloud는 그냥 training data입니다.
[학생이 질문]
질문은 바로 data cloud에서 ReLU가 죽어버리는지 아닌지를 어떻게 알 수 있냐느 것입니다.
여기 예를 다시 한 번 보겠습니다. 간단한 2차원의 경우입니다.
ReLU의 입력 값은 가령 W1*X1 + W2*X2 같은 값이 되겠죠
그리고 이 가중치가 초평면(초록 빨강)을 이루게 되겠죠
그리고 여기에서 positive인 절반만 가져갈 것입니다. 나머지 절반은 죽어버리겠죠
그러니 W의 초평면의 위치와 Data의 위치를 고려했을 때 초평면 자체가 Data와 동떨어지는 경우가 발생할 수 있습니다.
학습 과정에서 일부 ReLU는 data cloud와 동떨어져 있을 수가 있습니다.
질문 있나요?
[학생이 질문]
sigmoid의 두 가지 단점에 중에서 하나가 뉴런이 saturation 된다는 것이었습니다.
sigmoid 슬라이드를 다시한번 보겠습니다.
질문은 바로 input이 전부 positive면 saturation이 안되지 않냐는 것입니다.
여기 입력이 모두 양수면 0의 오른쪽 지역으로 들어오는 것이겠죠
그렇게 되도 여전히 saturation의 가능성이 있습니다.
왜냐하면 positive region에서도 여전히 평평한 곳이 존재하기 때문입니다.
양의 큰 값이 들어오면, 여기 평평한 지역 때문에 여전히 gradient가 0이 될 수 있을 것입니다.
좋습니다.
그래서 실제로 ReLU를 초기화할때 positive biases를 추가해 주는 경우가 있습니다.
Update 시에 active ReLU가 될 가능성을 조금이라도 더 높혀주기 위해서죠
하지만 이게 도움이 된다는 의견도 있고 그렇지 않다는 사람들도 있습니다.
이 방법을 항상 사용하지는 않고, 대부분은 사람들은 zero-bias로 초기화합니다.
ReLU 이후에 조금 수정된 버전이 나왔습니다.
그 중 하나가 바로 leaky ReLU 라는 녀석입니다.
ReLU와 유사하지만 negarive regime에서 더 이상 0이 아닙니다.
negative에도 기울기를 살짝 주게 되면 앞서 설명했던 문제를 상당 부분 해결하게 됩니다.
Leaky ReLU의 경우에는 negative space 에서도 saturation 되지 않습니다.
여전히 계산이 효율적입니다. 그리고 sigmoid 나 tanh보다 수렴을 빨리 할 수 있습니다. 그리고 ReLU랑도 비슷하게 생겼죠
그리고 dead ReLU 현상도 더 이상 없습니다.
또 다른 예시로는 parametric rectifier, PReLU 가 있습니다.
PReLU는 negative space에 기울기가 있다는 점에서 Leaky ReLU와 유사하다는 것을 알 수 있습니다.
다만 여기에서는 기울기가 alpha 라는 파라미터로 결정됩니다.
alpha를 딱 정해놓는 것이 아니라 backpro으로 학습시키는 파라미터로 만든 것입니다.
활성함수가 조금 더 유연해 질 수 있을 것입니다.
또 다른 예시로 ELU라는 것도 있습니다.
우리는 지금 LU 패밀리들을 보고 있습니다.
ELU는 ReLu의 이점을 그대로 가져옵니다.
하지만 ELU는 zero-mean 에 가까운 출력값을 보입니다.
zero-mean에 가까운 출력은 앞서 leaky ReLU, PReLU 가 가진 이점이었죠
하지만 Leaky ReLU와 비교해보면 ELU는 negative에서 "기울기" 를 가지는 것 대신에 또 다시 "saturation" 됩니다.
ELU가 주장하는건 이런 saturation이 좀더 잡음(noise)에 강인할 수 있다는 것입니다.
이런 deactivation이 좀더 강인함을 줄 수 있다고 주장합니다.
ELU의 논문에서는 왜 ELU가 더 강인한지에 타당성을 다양하게 제시하고 있습니다.
ELU는 ReLU와 Leaky ReLU의 중간 정도라고 보시면 됩니다.
ELU는 Leaky ReLU처럼 zero-mean의 출력을 내지만 Saturation의 관점에서 ReLU의 특성도 가지고 있습니다.
질문 있나요?
[학생이 질문]
질문은 바로 파라미터 alpha가 각 뉴런마다 제각각이냐는 것입니다.
아마 각각일 것 같은데 정확히 기억나지 않습니다. 논문에서 어떻게 정의하였는지를 보시면 될 것 같습니다.
하지만 확실한 것은 ELU가 좋은 장점을 지니도록 세심하게 디자인하였다는 것입니다.
지금까지는 ReLU의 변종들이었습니다.
다양한 활성함수들이 어떤 장단점이 있는지를 여러분 스스로 눈여겨 볼 수 있을 것입니다.
실제로 사람들이 이것저것 실험해보고 경험적으로 어떤 것이 더 좋은지를 살펴보고 새로운 것들을 만들어낸 것입니다.
이들 모두는 실험해볼만한 것들이라 할 수 있겠습니다.
그럼 하나만 더 알아보겠습니다. Maxout Neuron 이라는 것입니다.
지금까지 본 활성함수들과는 좀 다르게 생겼습니다. 입력을 받아드리는 특정한 기본형식을 미리 정의하지 않습니다.
대신에 w1에 x를 내적한 값 + b1과 w2에 x를 내적한 값 + b2 의 최댓값을 사용합니다.
Maxout은 이 두 함수 중 최댓값을 취합니다.
Maxout는 ReLU와 leaky ReLU의 좀 더 일반화된 형태입니다.
왜냐하면 Maxout은 이 두 개의 선형함수를 취하기 때문이죠
Maxout또한 선형이기때문에 saturation 되지 않으며 gradient가 죽지 않을 것입니다.
여기서 문제점은 뉴런당 파라미터의 수가 두배가 된다는 것입니다. 이제는 W1과 W2를 지니고 있어야합니다.
지금까지 다양한 활성함수를 살펴보았습니다. 실제로 가장 많이들 쓰는 것은 바로 ReLU 입니다.
ReLU가 표준으로 많이 사용되며 대게는 잘 동작합니다
다만 ReLU를 사용하려면 leraning rate를 아주 조심스럽게 결정해야 할 것입니다.
learning rate에 관해서는 이번 강의의 후반부에 좀 더 다뤄보도록 하겠습니다.
또한 여러분은 Leaky ReLU, Maxout, ELU와 같은 것들도 써볼 수 있겠습니다. 물론 이들은 아직 실험단계이긴 합니다.
여러분들의 문제에 맞춰 어떤 활성함수가 잘 동작하는지 확인해 볼 수 있을 것입니다.
tanh도 써볼 수 있겠죠. 하지만 대게는 ReLU와 ReLU의 변종들이 좀 더 잘 동작한다고 보시면 되겠습니다.
Sigmoid는 사용하지 않는 편이 좋습니다. 가장 구식이고 요즘은 LU 패밀리 계열이 더 잘 동작합니다.
자 그럼 이제 데이터 전처리에 대해 배워보겠습니다.
지금까지는 활성 함수에 대해 배워보았습니다.
이제는 실제로 네트워크를 훈련시켜 보려 합니다. 우선 우리에게는 입력 데이터가 있겠죠.
일반적으로 입력 데이터는 전처리를 해줍니다. Machine Learning 수업에서 다뤄 보신 분들도 계실 것입니다.
가장 대표적인 전처리 과정은 "zero-mean"으로 만들고 "normalize" 하는 것입니다.
normalization은 보통 표준편차로 합니다. 그렇다면 이걸 왜 하는 것일까요?
앞서 zero-centering에 대해 다룬 적이 있었죠. 입력이 전부 positive인 경우에 대해 말이죠
그러면 모든 뉴런이 positive인 gradient를 얻게 됩니다. 이는 최적하지 못한(suboptimal) 최적화가 됩니다.
데이터가 전부 positive 일 때 뿐만이 아니라 0 이거나 전부 negative일 경우에도 이런 일은 발생하곤 합니다.
normalization을 해주는 이유는 모든 차원이 동일한 범위안 있게 해줘서 전부 동등한 기여(contribute)를 하게 합니다.
실제로는 이미지의 경우 전처리로 zero-centering 정도만 해주니다.
normalization 하지는 않습니다. 왜냐하면 이미지는 이미 각 차원 간에 스케일이 어느정도 맞춰져 있기 때문입니다.
따라서 스케일이 다양한 여러 ML 문제와는 달리 이미지에서는 normalization을 엄청 잘 해줄 필요는 없습니다.
그리고 Machine learning 에서는 PCA나 whitening 같은 더 복잡한 전처리 과정도 있긴 합니다만
이미지에서는 단순히 zero-mean 정도만 사용하고 normalization 그 밖의 여러 복잡한 방법들은 잘 안씁니다.
일반적으로는 이미지를 다룰 때는 굳이 입력을 더 낮은 차원으로 projection 시키지 않습니다.
CNN에서는 원본 이미지 자체의 spatial 정보를 이용해서 이미지의 spatial structure를 얻을 수 있도록 합니다.
질문 있나요?
[학생이 질문]
질문은 Train Phase에서 전처리를 해줬으면 Test Phase 에서도 똑같은 전처리를 해 줘야 하는 것인 인데, 맞습니다.
일반적으로 Train에서 계산한 평균을 Test데이터에도 동일하게 적용해 줍니다. Training data에서 구한 평균을 그대로 사용하죠
요약해보자면 이미지를 다룰때는 기본적으로 zero-mean 으로 전처리를 해준다는 것입니다.
평균 값은 전체 Training data에서 계산합니다.
보통 입력이미지의 사이즈를 서로 맞춰주는데 가령 32x32x3 이고 네트워크에 들어가기 전에 평균값을 빼주게 됩니다.
그리고 Test time의 이미지에도 Training time에 계산한 평균 값으로 빼주게 됩니다.
실제로 일부 네트워크는 채널 전체의 평균을 구하지 않고 채널마다 평균을 독립적으로 계산하는 경우도 있습니다.
채널별로 평균이 비슷할 것인지 아니면 독립적으로 계산해야 할 것인지는 여러분이 판단하기 나름입니다.
AlexNet 이후 VGGNet 같은 네트워크가 이러한 형태인데 추후에 다시 다루도록 하겠습니다.
질문 있나요?
[학생이 질문]
질문은 두 가지 하셨습니다. 우선 첫 번째 질문은 "채널 의 의미" 입니다.
여기에서 채널은 RGB입니다. 이미지가 32x32x3 이면 Width(32) x Height(32) x RGB(3) 입니다.
VGG식으로 하면 RGB별로 각각 평균을 구하는 것입니다.
두 번째 질문이 무엇이었죠?
[학생이 질문]
질문은 "평균 을 어떻게 계산하는지" 입니다.
평균은 여러분의 "학습 데이터 전부" 를 가지고 계산합니다. 이해가 되셨습니까?
[학생이 질문]
질문은 만약 미니배치 단위로 학습시킨다고 해도 평균 계산은 미니배치 단위로 각각이 아니라 전체로 계산하냐는 것입니다.
맞습니다.
우리가 구하는 평균 이라는 것은 트레이닝 데이터의 평균입니다.
배치에서 뽑은 데이터도 사실 전체 데이터에서 나온 것들이죠 결국은 배치 평균이나 전체 평균이나 구해보면 같습니다.(이상적으로)
그러니 처음에 전체 트레이닝 이미지를 이용하여 단 한번만 평균을 계산 해 놓는 편이 낫습니다.
반대로 엄청 큰 데이터의 전체 평균을 구할때도 굳이 모든 데이터를 전부 쓰지 않고 적절하게 샘플링해서 구할수도 있겠죠
좋습니다. 혹시 다른 질문도 있으십니까?
[학생이 질문]
질문은 바로 데이터 전처리가 Sigmoid Problem을 해결할 수 있는지 입니다.
우리가 수행 할 전처리는 zero mean 입니다.
앞서 sigmoid 에게는 zero-mean이 필요하다고 했습니다.
데이터 전처리가 sigmoid의 zero-mean 문제를 단지 "첫 번째" 레이어에서만 해결할 수 있을 것입니다.
처음에는 입력 이미지가 zero-mean 이므로 해결 할 순 있겠으나
하지만 그 다음 레이어부터는 똑같은 문제가 반복될 것입니다. Deep network라면 점점 더 심해지겠죠
가면 갈수록 더 non-zero-mean 을 겪게 될 것입니다.
따라서 이미지 전처리가 Sigmoid에서의 문제를 해결하기에는 충분하지 않을 것입니다.
좋습니다. 이제 weight를 어떻게 초기화시켜야 하는지를 알아보겠습니다.
"Two Layer Neural Network" 예시를 한번 봅시다. 우리가 할 일은 가중치 업데이트입니다.
맨 처음에 어떤 초기 가중치들이 있겠죠 그리고 gradient를 계산해서 가중치를 업데이트할 것입니다.
첫 번째 질문입니다. "모든 가중치 = 0" 이면 어떻게 될까요? 모든 파라미터를 0으로 세팅하면 어떻게될까요
[학생이 대답]
잘 못들었습니다?
"모든 뉴런이 죽어버리고 업데이트가 되지 않을것이다"고 답했습니다. 정확한 답은 아닙니다.
그래도 잘 대답하신 부분은 "모든 뉴런이 <같은일> 을 한다는 것" 입니다. 그렇다고 다 죽어버리지는 않습니다.
입력 데이터들이 뉴런의 어떤 영역에 속할 것이고 죽지 않을 수도 있습니다.
하지만 중요한 것은 전부 똑같은 일을 할 것이라는 것입니다.
가중치가 0 이라서 모든 뉴런은 모두 다 같은 연산을 수행합니다.
출력도 모두 같을 것이고. 결국 gradient도 서로 같습니다.
결국 모든 가중치가 똑같은 값으로 업데이트 됩니다.
결국 모든 뉴런이 모두 똑같이 생기게 됩니다. 우리가 그다지 원하는 것은 아니죠
우리는 뉴런들이 서로 다르게 생기길 원하죠
이것이 바로 모든 가중치를 동일하게 초기화시키면 발생하는 일입니다.
"Symmetry breaking"이 일어날 수 없습니다.
질문 있나요?
[학생이 질문]
질문은 gradient는 가중치 뿐만 아니라 Loss의 영향도 받으므로 결국 각각의 gradient는 다를 것이지 않냐는 것입니다.
각 뉴런이 어떤 클래스에 연결되어 있는지에 따라 뉴런들이 서로 다른 loss를 가질 수는 있습니다.
하지만 네트워크 전체를 보면 많은 뉴런들이 동일한 가중치로 연결되어 있을 것입니다.
결국 모든 뉴런이 같은 방식으로 업데이트 될 것이고 이는 문제가 됩니다.
우선 초기화 문제를 해결하는 첫번째 방법은 임의의 작은 값으로 초기화하는 것입니다.
이 경우 초기 W를 표준정규분포(standard gaussian) 에서 샘플링합니다.
좀 더 작은 값을 위해 스케일링을 해줍니다. 0.01을 나눠 표준편차를 1e-2 즉 0.01로 만들어 줍니다.
이런 식으로 모든 가중치를 임의의 값으로 초기화합니다.
여러분의 네트워크가 작은 네트워크라면 symmetry breaking에 뭐 이정도면 충분합니다.
하지만 이 방법은 더 깊은 네트워크에서 문제가 생길 수 있습니다.
그래서 이것이 왜 그런지 살펴 보겠습니다. 이런 식으로 실험을 해 볼 수 있습니다.
조금 더 깊은 네트워크를 가지고 실험해봅시다.
여기 10개의 레이어로 이루어진 네트워크가 있습니다. 레이어당 500개의 뉴런이 있습니다.
nonlinearities로는 tanh를 사용해 봅시다
그리고 가중치는 "임의의 작은 값" 으로 초기화시킵니다.
데이터를 랜덤으로 만들어주고 이 데이터를 forward pass시켜보겠습니다.
그리고 각 레이어별 activations 수치를 통계화 시켜보겠습니다.
자 그럼 한번 분석을 해 보겠습니다. 가장 상단의 수치들은 글씨가 너무 작아서 잘 안보일 수도 있습니다.
어쨋든 이 결과는 각 레이어 출력의 평균과 평균과 표준편차를 계산한 것입니다.
우선 첫번째 레이어를 한번 봅시다. 평균은 항상 0 근처에 있습니다.(tanh 특성상)
[마이크 잡음 관련]
자 다시 여기 출력 분포들을 보면 당연하게도 평균은 항상 0 근처에 있습니다.
여기 코드를 한번 봅시다.
X와 W를 내적한 값에 tanh를 취합니다. 그리고 그 값을 저장합니다.
tanh가 zero-centered 이니까 평균이 0에 가까운건 당연하겠죠
하지만 표준편차를 보게되면 줄어듭니다. 아주 가파르게 줄어들어서 0에 수렴합니다.
여기 가운데 그래프를 보시면 레이어당 평균과 표준편차를 나타낸 것입니다.
그리고 맨 밑에 똑같은 결과를 분포로 표현했습니다.
첫 번째 레이어에서는 가우시안스럽게 생긴 좋은 분포를 형성하고 있는것을 볼 수 있습니다.
하지만 문제는 우리가 W를 곱하면 곱할수록 W가 너무 작은 값들이라서 출력 값이 급격히 줄어드는 것입니다.
그리고 결국 0이 되겠죠
우리가 원하는 것이 아닙니다.
그래서 모든 활성함수 결과가 0이됩니다.
자 그럼 backwards pass로 다시한번 생각해 봅시다.
방금 전까지 한 것을 forward pass했다고 가정해보고 이제 gradient를 구해봅시다.
우선 각 가중치들에 해당하는 gradient가 어떨 것이라고 다들 생각하십니까?
정답 아는분 있으신가요?
이렇게 생각해봅시다. 각 레이어의 "입력" 이 엄청 작은 것입니다.
입력 값이 점점 0에 수렴합니다. Backporp을 생각해보면 "upstream gradient"가 점점 전파됩니다.
자 그럼 현재 가중치를 업데이트하려면 "upstream gradient" 에 local gradient를 곱해주면 됩니다. 우선 WX를-
W에 대해 미분해보면 local gradient는 입력 X가 됩니다.
자 이렇게 생각해보면 앞서 다뤘던 문제와 유사한 문제가 생긴다는 것을 알 수 있습니다.
X가 엄청 작은 값이기 때문에 gradient도 작을 것이고 결국 업데이트가 잘 일어나지 않습니다.
이런 실험은 gradient의 흐름이 네트워크에 어떤 영향을 미치는지 생각해 볼 수 있는 좋은 예시입니다.
이제는 forward pass의 형태를 살펴보고는 이런 경우 gradient가 어떤 식으로 전파되는지 짐작 하실 수 있을 것입니다.
그리고 다양한 입력 타입에 따라 weight와 gradient가 어떤 영향을 미치는지도 생각해 볼 수 있겠습니다.
그리고 또한 gradients가 연결되면서(chaining) 어떤 식으로 전파(flowing back)되는지를 한번 생각해본다면
gradient를 backprop하는 과정은 그 반대입니다. upstream gradient에 W의 gradient인 X를 곱합니다.
그리고 입력 X은 어떤 내적(dot product)의 결과입니다.
그리고 backward pass의 과정에서 upstream gradient를 구하는 것은 현재 upstream에 가중치를 곱하는 것 입니다.
우리는 W를 계속해서 곱하기 때문에 Backwork pass에서도 Forward 처럼 점점 gradient값이 작아지게 됩니다.
따라서 upstream gradients는 0으로 수렴하게됩니다.
질문 있나요?
[학생이 질문]
예 물론 upstream과 downstream은 전혀 다른 것입니다.
이 경우에는 backpropb를 하는 것이니 backward pass를 한다고 보시면 됩니다.
upstream 은 gradient이 흘러간다고 보시면 됩니다. loss에서부터 시작해서 최초 입력까지 흘러가죠
upstream은 "우리가 이미 계산한 곳" 으로 부터 옵니다. 그리고 현재 노드를 계산하고 또 밑으로 내려가죠
계속 아래로 흐른다고 보시면 됩니다. 그리고 backprop 으로 노트로 흘러 들어온 것이 upstream 입니다.
자 지금까지는 가중치가 엄청 작을 때 발생하는 문제를 살펴보았습니다.
그렇다면 가중치를 좀 더 큰 값으로 초기화하면 어떨지 한번 생각해 볼 수 있을 것입니다.
자 그럼 가중치의 편차를 0.01 이 아니라 1로 하면 어떻게 될까요?
자 이제 가중치가 큽니다. 이렇게 큰 가중치를 통과한 출력 WX를 구하고 이를 tanh를 거치게 된다면 어떨까요?
앞서 tanh의 입력에 따라 어떤 일이 발생하는지 배웠습니다. 그렇다면 문제가 뭘까요?
맞습니다. 값들이 saturation 될 것입니다.
가중치가 큰 값을 가지므로 tanh의 출력은 항상 saturation될 것입니다.
그렇게 되면 다음과 같은 결과를 확인 해 볼 수 있는데. 출력이 항상 -1 이거나 +1 일 것입니다.
앞서 말했듯 값이 saturation 될 것이고 gradient는 0이 될 것입니다. 가중치 업데이트가 일어나지 않겠죠.
결국 적절한 가중치를 얻기는 너무 어렵습니다.
너무 작으면 사라져버리고 너무 크면 saturation되어 버립니다
그래서 사람들이 어떻게 하면 가중치 초기화를 잘 할 수 있을까 하고 열심히 고민하였습니다.
널리 알려진 좋은 방법 중 하나는 바로 Xavier initialization 입니다.
Glorot가 2010에 발표한 논문입니다.
자 그럼 맨 위에 W의 공식을 한번 보겠습니다.
Standard gaussian으로 뽑은 값을 "입력의 수" 로 스케일링해줍니다.
웹에 에된 Lecture note를 보시면 상세한 수식을 확인 해 보실 수 있습니다.
기본적으로 Xavier initialization가 하는 일은 입/출력의 분산을 맞춰주는 것입니다.
그리고 이 수식을 통해서 직관적으로 이해할 수 있는 것은
입력의 수가 작으면 더 작은 값으로 나누고 좀 더 큰 값을 얻습니다. 그리고 우리는 더 큰 가중치가 필요합니다. 왜냐하면-
작은 입력의 수가 가중치와 곱해지기 때문에, 가중치가 더 커야만 출력의 분산 만큼 큰 값을 얻을 수 있기 때문입니다.
반대로 입력의 수가 많은 경우에는 더 작은 가중치가 필요합니다.
더 자세한 내용은 Lecture Note를 참고하시기 바랍니다.
각 레이어의 입력이 Unit gaussian이길 원한다면 이런 류의 초기화 기법을 사용해 보실 수 있습니다.
각 레이어의 입력을 Unit gaussian스럽게(approximately) 만들어 줄 수 있습니다.
여기서 가정하는 것은 현재 Linear activation이 있다고 가정하는 것입니다.
가령 tanh의 경우 우리가 지금 tanh의 active region안에 있다고 가정하는 것입니다.
다시 한번 말씀드리지만 lecture note를 보시면 좀 더 자세한 이해를 하실 수 있을 것입니다.
하지만 문제가 하나 있습니다. ReLU를 쓰면 잘 동작하지 않는다는 것입니다.
ReLU를 쓰면 무슨 일이 일어나는지를 보면
ReLU는 출력의 절반을 죽입니다. 그 절반은 매번 0이 됩니다. 그래서 결국 출력의 분산을 반토막 내버립니다.
그러므로 이전과 같은 가정을 해버리면 ReLU에서는 잘 작동하지 않습니다. 값이 너무 작아지는 것입니다.
여기 보이는 것이 그런 현상인데 분포가 또 줄어들기 시작했습니다.
점점 더 많은 값들이 0이 되고 결국은 비활성(deactivated) 되버립니다.
이 문제를 해결하기 위한 일부 논문이 있는데 여기에서는 추가적으로 2를 더 나눠줍니다.
뉴런 들 중 절반이 없어진다는 사실을 고려하기 위해서 입니다.
일제 입력은 반밖에 안들어가므로 반으로 나눠주는 텀을 추가적으로 더해 주는 것이고 실제로 잘 동작합니다.
결과를 보시면 전체적으로 좋은 분포를 형성하고 있는것을 볼 수 있습니다.
실제로 이 작은 변화는 트레이닝에 있어서 엄청난 차이를 보입니다.
가령 일부 논문을 보면 그런 차이가 트레이닝이 정말 잘 되거나 하나도 안되거나를 결정하는 결과를 보이기도 합니다.
적절한 초기화는 여전히 활발이 연구되는 분야입니다.
관심 있는 분들은 이 논문과 리소스를 활용하시면 될 것입니다.
초기화는 우선 Xavier Initialization을 추천드리며 그 밖에 다른 방법을 시도해 보는 것도 좋을 것입니다.
gaussian의 범위로 activation을 유지시키는 것에 관련한 또 다른 아이디어를 하나 소개해 드리겠습니다.
우리는 레이어의 출력이 unit gaussian 이길 바랍니다. Batch normalization이라 불리는 이 아이디어는 -
그렇다면 그렇게 만들어보자 하는 것입니다. 그냥 강제로 그렇게 만들어보자
그럼 이것이 어떻게 동작할까요?
어떤 레이어로부터 나온 Batch 단위 만큼의 activations 이 있다고 했을때, 우린 이 값들이 Unit gaussian 이기를 원합니다.
누구나 이 데이터를 Unit Gaussian으로 (empirically) 만들 수 있습니다.
현재 Batch에서 계산한 mean과 variance를 이용해서 Normalization 해 볼 수 있을 것입니다.
가중치를 잘 초기화 시키는 것 대신에 학습 할 때 마다 각 레이어에 이런 일을 해줘서 모든 레이어가 Unit gaussian이 되도록 해줍니다.
그래서 결국 학습하는 동안 모든 레이어의 입력이 Unit gaussian이 됐으면 좋겠는 것입니다.
그래서 이제부터 할 일은 네트워크의 forward pass 동안에 그렇게 되도록 "명시적으로" 만들어 주는 것입니다.
각 뉴런을 평균과 분산으로 Normalization 해주므로서 이런 일을 함수로(functionally) 구현하는 것입니다.
Batch단위로 한 레이어에 입력으로 들어오는 모든 값들을 이용해서 평균과 분산을 구해서 Normalization 해주는 것입니다.
그리고 이런게 만큼 함수를 보면 미분 가능한 함수입니다.
평균과 분산을 "상수" 로 가지고만 있으면 언제든지 미분이 가능하며 따라서 Backprop이 가능하게 됩니다.
여기 보시면 Batch당 N개의 학습 데이터가 있고 각 데이터가 D차원이라고 해봅시다.
그리고 각 차원별로(feature element별로) 평균을 각각 구해줍니다.
한 Batch 내에 이걸 전부 계산해서 Normalize 합니다.
그리고 이 연산은 FC나 Cov Layer 직후에 넣어줍니다.
깊은 네트워크에서 각 레이어의 W가 지속적으로 곱해져서 Bad scaling effect가 발생했습니다만
Normalization은 그 bad effect를 상쇄시켜 버립니다.
BN은 입력의 스케일만 살짝 조정해 주는 역할이기 때문에 FC와 Conv 어디에든 적용할 수 있습니다.
Conv Layer에서 차이점이 있다면 Normalization을 차원마다 독립적으로 수행하는 것이 아니라
같은 Activation Map의 같은 채널에 있는 요소들은 같이 Normalize 해 줍니다.
왜냐하면 Conv 특성상 (convolutional property) 같은 방식으로 normalize 시켜야 하기 때문입니다.
Conv Layer의 경우 Activation map(채널, Depth) 마다 평균과 분산을 하나만 구합니다.
그리고 현재 Batch 에 있는 모든 데이터로 Normalize 해줍니다.
Batch Normalization는 숙제로 나갈 것입니다.
Batch Normalization 2015년도 논문에 더 자세하게 설명이 되어 있습니다.
Batch Normalization 은 아주 유용한 기법이고 실제로 정말 많이 사용하는 방법입니다.
BN을 사용하려면 논문을 읽어보셔야 합니다. BN의 흐름도 한번 살펴보시고
BN에서 Gradient를 어떻게 구하는 지도 한번 살펴보시길 바랍니다.
그런데 한가지 문제가 있습니다.
FC layer를 거칠 때 마다 매번 normalization를 해주는 것에 대한 의문이 있습니다.
우리가 정말 tanh의 입력이 unit gaussian 이기를 바라는 것일까요?
normalization이 하는 일은 입력이 tanh의 linear한 영역에만 존재하도록 강제하는 것입니다.
그렇게 되면 saturation이 전혀 일어나지 않게 됩니다.
saturation 이 전혀 일어나지 않는것 보다는 "얼마나" saturation이 일어날지를 우리가 조절하면 더 좋겠죠
Batch normalization에서는 normalization연산이 있었죠 (왼쪽 상단 Narmalize: 수식)
그 이후에 BN에서는 scaling 연산을 추가합니다.
이를 통해 Unit gaussian으로 normalize 된 값들을 감마로는 스케일링의 효과를, 베타는 이동의 효과를 줍니다.
이렇게 하면 normalized 된 값들을 다시 원상복구 할 수 있도록 해줍니다. 그렇게 하고 싶다면 말이죠
네트워크가 값 들을 원상복구 하고 싶다면 "감마 = 분산", "베타 = 평균" 로 하면 됩니다.
BN을 하기 전 값으로 돌아가는 것입니다.
네트워크가 데이터를 tanh에 얼마나 saturation 시킬지를 학습 하기 때문에 우리는 유연성을 얻을 수 있습니다.
Batch normalization를 다시한번 요약해 보자면
입력이 있고 우리는 mini-batch에서의 평균을 계산합니다. 모든 mini-batch 마다 각각 계산해 줍니다. 분산도 계산합니다.
그리고 평균과 분산으로 Normalize 한 이후에 다시 추가적인 scaling, shifting factor를 사용합니다.
BN은 gradient의 흐름을 보다 원활하게 해주며 결국 더 학습이 더 잘되게(robust) 해줍니다.
BN을 쓰면 learning rates를 더 키울 수도 있고 다양한 초기화 기법들도 사용해 볼 수 있습니다.
그래서 사람들이 BN을 쓰면 학습이 더 쉬워진다고 합니다. BN를 써야하는 이유죠.
그리고 또 하나 짚고 넘어가야 할 것은 BN이 regularization의 역할도 한다는 것입니다.
각 레이어의 출력은 해당 데이터 하나 뿐만 아니라 batch 안에 존재하는 모든 데이터들에 영향을 받습니다.(평균, 분산)
왜냐하면 각 레이어의 입력은 해당 배치의 (표본)평균으로 Normalize 되기 때문입니다.
그렇기 때문에 이 레이어의 출력은 이제 오직 하나의 샘플에 대한 deterministic한 값이 아니게 되고
Batch 내의 모든 데이터가 입력으로 한대 묶인다고 볼 수 있습니다.
그러므로 더 이상 레이어의 출력은 deterministic하지 않고 조금씩 바뀌게 되고 이는 regularization effect를 줍니다.
질문있나요?
[학생이 질문]
질문은 감마와 베타가 학습가능한 파라미터 냐는 것인데 예 맞습니다.
[학생이 질문]
질문은 바로 왜 감마와 베타를 학습시켜서 다시 identity function(Y = X)가 되게 하는 것인지 압니다.
이렇게 하는 이유는 유연성을 위해서 입니다.
batch normalization이 하는 일을 생각해보면 레이어의 입력이 unit gaussian이 되도록 강제하는 것입니다.
일반적으로 이렇게 Normalize 하는 것이 좋긴 하지만 그렇다고 항상 좋은 것은 아닙니다.
tanh의 예시에서도 보았듯이 여러분은 입력데이터가 tanh로 인해 얼마나 saturation 될지를 조절하고 싶을 수도있습니다.
이런 유연성을 통해서 gaussian normalization이 필요하면 그렇게 해주고 또 일부 그 방법이 최선이 아닐때가 있으면 -
unit gaussian을 조금씩 조금씩 scaling 하고 shifting 할 수 있는 여지를 주는 것입니다.
감마와 베타라는 파라미터는 이런 추가적인 유연성을 제공해 줍니다.
때문에 batch 단위로 normalization을 일단 해주고 파라미터를 다시 학습시키는 것입니다.
[학생이 질문]
예 맞습니다. 각 뉴런의 출력입니다. 가령 FC Layer의 출력으로 Wx가 있다고 해보면
출력으로 나온 각 뉴런마다 각각 batch normalization을 적용하는 것입니다.
질문 있나요?
[학생이 질문]
질문은 바로 reinforcement learning와 같이 batch 사이즈가 작은 경우 어떻게 해야 하는지 입니다.
실제로 BN은 일반적인 CNN에 정말 널리 쓰이고 있습니다.
그리고 recurrent / reinforcement learning 등 다양한 네트워크에 어떻게 적용하는지에 대한 많은 논문들이 있습니다.
이런 네트워크에는 또 다른 고려사항이 있겠죠. 현재도 아주 활발에 연구되는 분야입니다.
일부 논문은 나중에 살펴볼 예정이지만 대부분의 CNN에서 BN은 아주 잘 동작합니다.
Batch 사이즈가 작으면 정확도가 떨어지긴 하겠지만 여전히 비슷한 효과를 주긴 합니다.
Batch 사이즈가 작은 경우 더 많은 샘플들을 이용해서 평균과 분산을 구할 수 있도록 다시 디자인하는 방법도 있을 것입니다.
실제로는 이런 경우가 흔하지는 않습니다. 하지만 Batch가 작아서 문제가 될 때 도움을 줄 수는 있을 것입니다.
질문 있나요?
[학생이 질문]
질문은 바로 우리가 입력을 gaussian 이라고 강제해 버리면 기존의 구조(structure)를 잃는 것 아닌지 입니다.
우선은 아닙니다. 데이터의 전처리를 할때도 gaussian을 쓰죠 모든 입력들(features) 가우시안 분포로 만든다고 해도 -
어떠한 구조도 잃지 않습니다.
데이터에 "연산이 잘 수행되도록" 우리는 그저 데이터를 조금만 움직이고 스케일링 하는 것입니다. (선형변환임)
CNN을 보면 우리는 데이터의 "공간적인 구조" 가 잘 유지되길 바랍니다.
Conv Layer 출력인 activation maps이 그런 "공간적 구조" 를 유지하고 있기를 원합니다.
이 점을 고려해서 CNN Activation Map을 normalize할 때는 전체 map의 평균과 분산을 같이 구합니다.
Activation Map 당 평균과 분산을 하나씩만 구하는 것입니다.
이와 관련된 사항은 BN 논문에도 잘 설명되어 있으며 여러분이 과제로도 해야 할 것입니다.
질문 있나요?
[학생이 질문]
질문은 우리가 "레이어의 가중치"를 Normalize 하는 이유 가 gaussian으로 만들기 위해서 인지 입니다.
질문을 좀 정정하자면 "레이어의 가중치" 가 아니라 "레이어의 입력" 이죠
우선 "레이어의 가중치" 가 아니라 "레이어의 입력"을 Normalize하는 것입니다.
[학생이 질문]
질문은 "레이어의 입력"에 평균을 빼고 표준편차로 나눠주면 gaussian이 되냐는 것입니다. 예 맞습니다.
Normalize 를 할때 데이터에 어떤 일이 발생하는지 한번 생각해봅시다. 우선 mean 만큼 이동하죠 그러면 zero-centered가 될 것입니다.
그리고 표준편차로 스케일링합니다. 이 과정을 거치면 Unit gaussian으로 변환되는 것입니다.
이에 대한 자세한 내용은 기계학습을 설명하는 많은 자료들이 있으니 찾아보시기 바랍니다.
실제로 어떤 일이 일어나는지를 시각화한 자료도 있습니다.
하지만 기본적으로 normalize 가 하는 일은 여러분의 데이터를 gaussian distribution으로 만들어 주는 것입니다.
질문있나요?
[학생이 질문]
어 허.
질문은 shift와 scale요소를 추가시키고 학습을 시켜버리면 결국 identity mapping이 되서 BM이 사라지는 것 아닌지 입니다.
네트워크가 BM이 쓸모없다고 판단해서 identity mapping으로 만들어 버린다면 물론 그렇게 되겠지요
하지만 실제로는 그런 일은 일어나지 않습니다. 실제로 감마와 베타를 학습시키게 되면 identity 가 되지는 않습니다.
shift와 scale이 일정량 변하긴 하지만 보통은 dentity mapping이 될 정도는 아닙니다.
때문에 여전히 batch normalization의 효과를 얻을 수 있습니다.
수업자료에 identity mapping를 넣은 이유는 극단의 경우에 가능하다는 것이지 실제로는 일어나지 않습니다.
질문있나요?
[학생이 질문]
질문을 잘 이해한 것인지 모르겠지만 이전에 있었던 질문과도 상당히 유사한 것 같네요
우리가 지금 하는 일은 unit gaussian으로 맞춰주는 것이죠 그렇다고 실제 데이터가 gaussian이 되는 것은 아닙니다.
입력 데이터가 당연히 완전한 gaussian 분포일 수는 없겠죠 다만 gaussian과 비슷하다(approximately)고 가정하고나서 -
BM이 잘 동작하기를 기대하는 것입니다. 실제로 입력이 Gaussian을 따르지는 않습니다.
자 batch normalization에 대해 마지막으로 말씀드릴 것은 Test time에서 어떻게 동작하는지 입니다.
BM에서 평균과 분산은 학습데이터에서 구한 것입니다. Test time에 추가적인 계산은 하지 않습니다.
Training time에 running averages 같은 방법으로 평균 분산을 계산하고 Test time에 사용합니다.
자 이제 학습과정을 다루는법에 대해 알아보겠습니다.
지금까지 네트워크 설계를 배웠습니다. 이제는 학습과정을 어떻게 모니터링하고 하이퍼파라미터를 조절할 것인지를 배워보겠습니다.
좋은 학습 결과를 얻기 위해서 말이죠
우선 가장 첫 단계는 데이터 전처리입니다.
앞서 배운것 처럼 zero-mean을 사용합니다.
그리고 아키텍쳐를 선택해야합니다. 우선 하나의 Hidden Layer 와 50개의 뉴런을 가진 모델로 해봅시다.
그 밖에 어떤 모델을 선택해도 상관없습니다.
이제 해야할 일은 네트워크를 초기화시키는 것입니다.
Forward pass를 하고난 후에 Loss가 그럴듯해야 합니다.
지난 강의에서 배웠듯이 가령 우리가 softmax를 사용하고자 한다면
우리는 가중치가 작은 값일 때 Loss가 대강 어떻게 분포해야 하는지를 이미 알고있습니다.
Softmax classifier의 Loss는 negative log likelihood 가 되어야 합니다.
가령 10개의 클래스라면 Loss는 -log(1/10) 이 될 것입니다.
여기 보시면 약 2.3 정도 되는 것을 보실 수 있습니다. 이를 통해 Loss가 원하는 대로 동작한다는 것을 알 수 있습니다.
이 방법은 언제든 사용할 수 있는 유용한 방법입니다. (Good sanity check)
자 그럼 초기 Loss가 정상이라는 것을 확인했다면
지금까지는 여기 regularization을 0으로 설정하였습니다. 앞서 계산한 2.3은 regularization을 사용하지 않은 상태입니다.
여기에 regularization을 추가하면 Loss가 증가합니다. 손실함수에 regularization term이 추가되기 때문입니다.
이 또한 유용한 sanity check입니다.
자 이제 학습을 진행할 준비가 끝났습니다. 이제 학습을 한번 시작해봅시다.
처음 시작할 때 좋은 방법은 데이터의 일부만 우선 학습시켜 보는 것입니다.
데이터가 적으면 당연히 Overfit이 생길 것이고 Loss가 많이 줄어들 것입니다.
이때는 regularization를 사용하지 않습니다. 그리고 Loss가 내려가는지를 확인하는 것입니다.
여기 처럼 Epoch마다 Loss가 잘 내려가는지를 보는 것입니다. Loss가 0을 향해 꾸준히 잘 내려가는지를 확인해 봅니다.
Loss가 내려감과 동시에 Train Accuracy는 점점 증가합니다. 당연하겠죠
데이터가 작은 경우라면 모델이 완벽하게 데이터를 overfit 할 수 있어야 합니다.
자 지금까지의 sanity checks가 잘 끝났다면 이제부터는 정말로 학습을 시작할 차례입니다.
이제는 전체 데이터 셋을 사용할 것이고 regularization 을 약간만 주면서 적절한 learning rate를 찾아야 합니다.
Learning rate는 가장 중요한 하이퍼파라미터 중 하나입니다. 가장 먼저 정해야만 하는 하이퍼파라미터 입니다.
우선 learning rate 몇 가지를 정하고 실험해 봅니다. 여기에서는 1e- 6으로 정해 봤습니다.
그 결과로는 loss가 좀처럼 변하지 않는 것을 알 수 있습니다.
Loss가 잘 줄어들지 않는 가장 큰 요인은 Learning rate가 지나치게 작은 경우입니다.
Learning rate가 지나치게 작으면 gradient 업데이트가 충분히 일어나지 않게 되고 cost가 안변하게 됩니다.
여기에서 유심히 살펴봐야 할 점은 loss가 잘 변하지 않음에도 training/validation accuracy가 20%까지 급상승합니다.
혹시 왜 이런지 대답해 줄 분 계신가요?
현재 Softmax를 사용하고 있고 loss가 크게 안변했음에도 Accuracy는 올라간 상태입니다.
현재 상황은 확률 값들이 아직까지 멀리 퍼져있고 때문에 loss는 여전히 비슷비슷한 것입니다.
하지만 이 확률은 조금씩 "옮은" 방향으로 바뀌고 있습니다. 우리가 지금 "학습" 을 하고 있기 때문이죠
가중치는 서서히 변하고 있지만 Accuracy는 갑자기 뛰어버릴 수 있습니다.
Accuracy는 그저 가장 큰 값만 취하기 때문에 Accuracy가 크게 뛸 수 있는 것입니다.
자 이제 learning rate를 좀 바꿔봅시다. 더 큰 값인 1e-6를 1e6로 한번 바꿔보겠습니다.
자 결과를 보면 cost가 NaNs임을 알 수 있습니다. NaNs이라는 것은 cost가 발산(exploded)한 것입니다.
주된 이유는 learning rate가 지나치게 높기 때문입니다.
이 경우에는 learning rate를 낮춰야만 합니다. 그래서 3e-3으로 했는데, 여전히 발산합니다.
보통 learning rate는 1e-3 에서 1e-5 사이의 값을 사용합니다.
이 범위 사이의 값을 이용해서 cross-validation 을 수행해 줍니다.
이 사이의 값들을 이용해서 learning rate가 지나치게 작은지 아니면 큰지를 결정할 수 있을 것입니다.
자 그럼 하이퍼파라미터는 어떻게 정해줘야 할까요
하이퍼 파라미터를 최적화시키고 그중 가장 좋은 것을 선택하려면 어떻게 해야 할까요?
취할 수 있는 한가지 전략은 바로 cross-validation 입니다.
Cross-validation은 Training set으로 학습시키고 Validation set으로 평가하는 방법입니다.
이 hyperparameter가 잘 동작하는지를 확인해봅니다. 아마 과제에 있어서 이미 해보신 분도 계실 것입니다.
hyperparameter를 좀 더 단계적으로 찾아봅시다.
우선 coarse stage 에서는 넓은 범위에서 값을 골라냅니다. Epoch 몇 번 만으로도 현재 값이 잘 동작하는지 알 수 있습니다.
Epoch이 많지 않아도 어떤 하이퍼파라미터가 좋은지 아닌지를 알 수 있습니다.
Nan이 뜨거나 혹은 Loss가 줄지 않거나 하는 것을 보면서 이에 따라 적절히 잘 조절할 수 있을 것입니다.
coarse stage가 끝나면 어느 범위에서 잘 동작하겠다를 대충 알게 될 것입니다.
두 번째 fine stage에서는 좀 더 좁은 범위를 설정하고 학습을 좀 더 길게 시켜보면서 최적의 값을 찾습니다.
NaNs 으로 발산하는 징조를 미리 감지할 수도 있습니다. Train 동안에 Cost가 어떻게 변하는 지를 살펴보는 것입니다.
이전의 cost보다 더 커지거나 한다면, 가령 이전보다 cost가 3배 높아졌다거나 하면 잘못 하고 있는 것입니다.
Cost가 엄청 크고 빠르게 오르고 있다면 loop를 멈춰버리고 다른 하이퍼파라미터를 선택하면 됩니다.
자 예시를 한번 보겠습니다. 이는 5 epochs을 돌며 coarse search를 하는 과정입니다.
네트워크 구조는 앞서 만든 것과 유사합니다. 여기에서 확인 해야 할 것은 validation accuracy 입니다.
높은 val_acc에는 빨간색으로 표시를 해 놨습니다.
빨간 색으로 표시해 둔 지역이 바로 fine-stage를 시작할 만한 범위가 될 것입니다.
한 가지 주목할 점은 하이퍼 파라미터 최적화 시에는 Log scale로 값을 주는 것이 좋습니다.
파라미터 값을 샘플링할때 10^-3 ~ 10^-6 을 샘플링하지 말고 10의 차수 값만 샘플링하는 것이 좋습니다.(-3 ~ -6)
왜냐하면 learning rate는 gradient와 곱해지기 때문에
learning rate의 선택 범위를 log scale을 사용하는 편이 좋습니다.
따라서 차수(orders of magnitude)을 이용하는 편이 좋습니다.
자 그럼 범위를 다시한번 조절해 보겠습니다 (오른쪽)
reg는 경우를 보면 범위를 10^-4 에서 10^0 정도로 좁히면 좋을 것 같습니다. 나쁘지 않은 방법입니다.
이쪽 범위에서 53%의 val_acc를 보이는 것을 알 수 있습니다. 학습이 가장 잘 되는 구간이 될 것입니다.
하지만 여기에는 문제가 하나 있습니다.
자 우리는 여기에서 가장 좋은 Accuracy를 찾았습니다. (화살표)
잘 보면 good learning rates는 전부 10E-4 사이에 존재하고 있습니다.
Learning rate의 최적 값들이 우리가 다시 좁혀 설정한 범위의 경계부분에 집중되어 있다는 것을 알 수 있습니다.
이것은 좋지 않죠
이렇게 되면 최적의 Learning rate를 효율적으로 탐색할 수 없을 수도 있기 때문입니다.
실제로 최적의 값이 1E-5에 근처에 존재할 수도 있는 것입니다. 어쩌면 1E-6 쯤에 있을지도 모릅니다.
탐색 범위를 조금 이동시킨다면 더 좋은 범위를 찾을 수 있을지도 모릅니다.
당연히 여러분은 최적의 값이 내가 정한 범위의 중앙 쯤에 위치하도록 범위를 잘 설정해 주는 것이 중요합니다.
하이퍼파라미터를 찾는 또 다른 방법은 grid search를 이용하는 것입니다.
하이퍼 파라미터를 고정된 값과 간격으로 샘플링하는 것입니다.
하지만 실제로는 grid search보다는 이전 처럼 random search를 하는 것이 더 좋습니다.
Random search를 하는 경우 여기 오른쪽에 있는 것과 같은 결과를 얻을 수 있을 것입니다.
Random이 더 좋은 이유는 바로 내 모델이 어떤 특정 파라미터의 변화에 더 민감하게 반응을 하고 있다고 생각해 보면 (노란 < 초록)
이 함수가 더 비효율적인 dimentionality를 보인다고 할 수 있고 (노랑 에는 별로 영향을 안 받음)
Random search는 중요한 파라미터(초록) 에게도 더 많은 샘플링이 가능하므로
위에 그려놓은 초록색 함수를 더 잘 찾을 수 있을 것입니다.
Grid Layout 에서는 단지 세 번의 샘플링 밖에 할 수 없으므로 Good region이 어디인지 제대로 찾을 수 없습니다.
Random search를 사용하면 important variable 에서 더 다양한 값을 샘플링 할 수 있으므로 더 좋습니다.
하이퍼파라미터에는 어떤 것이 있을까 생각해보면 Learning rate가 있겠죠 이미 다뤄봤습니다.
그 밖에 decay schedule, update type, regularization, network architecture,
히든 유닛과 depth의 수 등 이 모든 하이퍼파라미터를 전부 다 최적화 시킬 수 있습니다.다.
일부는 이번 시간에 배운 내용이고 나머지는 다음 강의에서 좀 더 다뤄보도록 하겠습니다.
이 과정은 마치 턴테이블에 knobs를 조절하는 과정이라 할 수 있습니다.
여러분은 neural networks practitioner 손실함수의 출력이 음악이라고 생각해 보면
적절한 출력(loss)을 내보이기 위해서 모든 것들을 적절히 조절하는 것이라고 보시면 될 것입니다.
때문에 이는 일종의 art라고 할 수 있습니다.
실제로 여러분은 하이퍼파라미터 최적화와 Cross-validation 정말 많이 해야 할 것입니다.
cross validation으로 엄청나게 많은 하이퍼파라미터를 직접 돌려보고 모니터해서 어떤 값이 좋고 나쁜지를 확인해야 합니다.
여기 있는 loss curve를 보면서 좋은 것을 찾아서 시도해보는 일을 계속 반복해야 합니다.
loss curve를 모니터링 하는데 있어서 앞서 언급했듯 learning rate이 정말 중요합니다.
그리고 loss curve를 보고 어떤 learning rate가 좋고 나쁜지를 알아볼 수 있습니다.
Loss가 발산하면 learning rate가 높은 것이고 너무 평평(linear)하다 싶으면 너무 낮은 것입니다.
가파르게 내려가다가 어느순간 정체기가 생기면 이 또한 여전히 너무 높다는 의미입니다.
learning step이 너무 크게 점프해서 적절한 local optimum 에 도달하지 못하는 경우입니다.
최적의 learning rate는 대게 이런식입니다 (파란색) 비교적 가파르게 내려가면서도
지속적으로 잘 내려간다면 현재 learning rate를 유지해도 좋습니다.
이들은 모두 여러분들이 실제 학습을 할 때 겪게 될 것들입니다.
끝날 시간이 거의 다 되서 한 가지만 더 알아보겠습니다. loss curve에 관한 것입니다.
loss가 평평하다가 갑자기 가파르게 내려가는 것을 보게 된다면 이는 초기화의 문제일 수 있습니다.
gradient의 backpropb이 초기에는 잘 되지 않다가 학습이 진행되면서 회복이 되는 경우입니다.
그리고 다음은 여러분이 accuracy를 모니터링 하다보면 겪을 수 있는 일입니다.
만일 train_acc와 va_acc가 큰 차이를 보인다면
이를 overfit 일 지도 모릅니다. 따라서 regularization 의 강도를 높혀야 할 지도 모릅니다.
gap이 없다면 아직 overfit하지 않은 것이고 capacity을 높힐 수 있는 충분한 여유가 있다는 것을 의미합니다.
그리고 보통 우리는 가중치의 크기 대비 가중치 업데이트의 비율을 지켜볼 필요가 있습니다.
우선 파라미터의 norm을 구해서 가중치의 규모를 계산합니다.
그리고 업데이트 사이즈 또한 norm을 통해 구할 수 있고 이로 얼마나 크게 업데이트 되는지를 알 수 있습니다.
우리는 이 비율이 대략 0.001 정도 되길 원합니다.
이 값은 변동이 커서 정확하지 않을 수 있습니다.
하지만 업데이트가 지나치게 크거나 작은지에 대한 감을 어느정도 가질 수는 있습니다.
너무 업데이트가 지나치거나 아무 업데이트도 없으면 안되겠죠
여러분의 문제가 뭔지 디버깅할 때 유용할 것입니다.
자 오늘 배운 내용을 요약해보면 활성 함수와 데이터 전처리 그리고 가중치 초기화, Batch Norm, 학습 과정 준비하기
하이퍼 파라미터 최적화를 배웠습니다.
각각의 주제에서 명심해야 할 것들이 있었습니다.
ReLU를 사용하고(활성함수), 평균을 뺴고(데이터 전처리), Xavier Initialization을 쓰고 (가중치 초기화)
Batch Norm을 사용하고 Random Search를 통해 하이퍼파라미터를 찾습니다.
다음 시간에는 다른 주제로 Neural Network에 대해 조금 더 공부해 보겠습니다. 감사합니다